package com.encryption.service.ca.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.util.encoders.Base64;


/**
 * �������ڻ�ȡ֤��ʵ��֮��᲻����չ
 * @author yitao.song
 * @version $Id: AlipayCertificate.java,v 0.1 2011-3-16 ����09:01:02 yitao.song Exp $
 */
public class CertificateUtil extends X509CertificateObject {
    
    
    /**
     * ��ι��캯��
     * @param c
     * @throws CertificateParsingException
     */
    public CertificateUtil(X509CertificateStructure c) throws CertificateParsingException {
        super(c);
    }
   
    /**
     * ���ļ��л�ȡ<code>X509Certificate</code>ʵ��
     * @param certFileName
     * @return
     * @throws Exception
     */
    public static X509Certificate getInstanceFromFile(String certFileName) throws Exception {
    	String userDir = System.getProperty("user.dir");
        FileInputStream fis = new FileInputStream(userDir + "/" +certFileName);
        return engineGenerateCertificate(fis);
    }

    /**
     * ���������л�ȡ<code>X509Certificate</code>ʵ��
     * @param in
     *          ������
     * @return
     * @throws Exception
     *          �׳����쳣
     */
    public static X509Certificate getInstance(InputStream in) throws Exception {
        return engineGenerateCertificate(in);
    }

    /**
     * ��PEM֤���ַ���<code>X509Certificate</code>ʵ��
     * @param pemCertString
     *          PEM֤���ַ�
     * @return      
     * @throws Exception
     *          �׳����쳣
     */
    public static X509Certificate getInstance(String pemCertString) throws Exception {
        ASN1InputStream in = new ASN1InputStream(pemCertString.getBytes());
        return engineGenerateCertificate(in);
    }

    /**
     * ΪDER����ASN.1�ֽ���������� <code>X509Certificate</code>ʵ��
     * @param derCertEncoded
     * @return
     * @throws Exception
     *          �׳����쳣
     */
    public static X509Certificate getInstance(byte[] derCertEncoded) throws Exception {
        ASN1InputStream in = new ASN1InputStream(derCertEncoded);
        return engineGenerateCertificate(in);
    }

    /**
     * ���������л�ȡ<code>X509Certificate</code>ʵ��
     * @param in
     *       ������
     * @return
     * @throws Exception
     */
    private static X509Certificate engineGenerateCertificate(InputStream in) throws Exception {
        try {
            if (!in.markSupported()) {
                in = new BufferedInputStream(in);
            }

            in.mark(10);
            int tag = in.read();

            if (tag == -1) {
                return null;
            }

            if (tag != 48) {
                in.reset();
                return readPEMCertificate(in);
            }
            if (in.read() == 128) {
                in.reset();
                return readPKCS7Certificate(in);
            }
            in.reset();
            return readDERCertificate(in);
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

    /**
     * ��P7������ֽ����л�ȡ<code>X509Certificate</code>ʵ��
     * @param in
     * @return
     * @throws IOException
     * @throws CertificateParsingException
     */
    private static X509Certificate readPKCS7Certificate(InputStream in) throws IOException,
                                                                       CertificateParsingException {
        ASN1InputStream dIn = new ASN1InputStream(in);
        ASN1Sequence seq = (ASN1Sequence) dIn.readObject();

        return new CertificateUtil(X509CertificateStructure.getInstance(seq));
    }

    /**
     * ��PEM�ļ����������л�ȡ<code>X509Certificate</code>ʵ��
     * @param in
     * @return
     * @throws IOException
     * @throws CertificateParsingException
     */
    private static X509Certificate readPEMCertificate(InputStream in) throws IOException,
                                                                     CertificateParsingException {
        StringBuffer pemBuf = new StringBuffer();
        String line;
        while ((line = readLine(in)) != null) {
            if ((line.equals( "-----BEGIN CERTIFICATE-----"))
                || (line.equals( "-----BEGIN X509 CERTIFICATE-----"))
                || (line.equals("-----END CERTIFICATE-----"))
                || (line.equals( "-----END X509 CERTIFICATE-----"))) {
                continue;
            }
            pemBuf.append(line);
        }

        if (pemBuf.length() != 0) {
            ByteArrayInputStream bIn = new ByteArrayInputStream(Base64.decode(pemBuf.toString()));
            return readDERCertificate(bIn);
        }

        return null;
    }

    /**
     * ��DER�ļ����������л�ȡ<code>X509Certificate</code>ʵ��
     * @param in     *         
     * @return
     * @throws IOException
     * @throws CertificateParsingException
     */
    private static X509Certificate readDERCertificate(InputStream in) throws IOException,
                                                                     CertificateParsingException {
        ASN1InputStream dIn = new ASN1InputStream(in);
        ASN1Sequence seq = (ASN1Sequence) dIn.readObject();

        return new CertificateUtil(X509CertificateStructure.getInstance(seq));
    }

    /**
     * �ο�BC��ʵ��readLine
     * @param in
     * @return
     * @throws IOException
     */
    private static String readLine(InputStream in) throws IOException {
        StringBuffer l = new StringBuffer();
        int c;
        while (((c = in.read()) != 10) && (c >= 0)) {
            if (c == 13) {
                continue;
            }
            l.append((char) c);
        }

        if ((l.length() == 0) && (c < 0)) {
            return null;
        }

        return l.toString();
    }
}
